home *** CD-ROM | disk | FTP | other *** search
/ MacHack 1996 / MacHack 1996.toast / Hacks / Hacks ’89 / gadlife / source (ugly) / step.c < prev    next >
Encoding:
C/C++ Source or Header  |  1989-05-10  |  13.9 KB  |  469 lines  |  [TEXT/KAHL]

  1. /*******************************************************
  2. *                                                                *
  3. *    step.c takes care of stepping the data in a window, and contains doBackground,    *
  4. *    the main routine to this effect.  This and finishCopy should be the only routines    *
  5. *    called from the outside, unless some special effect is desired.                *
  6. *                                                                *
  7. *******************************************************/
  8.  
  9. #include "main.h"
  10.  
  11. extern WindowPtr    frontUserWindow(),
  12.                 messyLevel;
  13. extern dataHandle    currentData;
  14. extern int            isBackground;
  15.  
  16. long                lifeSpeed = 260,    /* These ought to be computed at startup.    */
  17.                 copySpeed = 512;
  18.  
  19. /*******************************************************
  20. *                                                                *
  21. *    Obnoxious code - runs life for a certain number of longwords.  It starts with    *
  22. *    pointers for the current line, the previous line, and the next line, along with    *
  23. *    a number of longwords to compute.  It works in the negative direction, and at    *
  24. *    the end has a longword left over, which should be stored or passed on the next    *
  25. *    call as oldval.  It operates via a logic function on 32 pixels at once, resulting    *
  26. *    in a total speed of about 4 cycles (68020) per pixel, or 4Mpx/sec.            *
  27. *                                                                *
  28. *******************************************************/
  29.  
  30. long lifeLines( counts, prev, curr, next, dest, oldval )
  31.     long        counts, *prev, *curr, *next, *dest, oldval;
  32.     {
  33.     asm        {
  34.             movem.l    d3-d7/a2-a4,-(sp)
  35.             movem.l    counts,d7/a0-a4
  36.             subq        #1,d7            
  37.             clr.b        d5
  38. load:            move.l    -(a0),d0
  39.             move.l    -(a2),d1
  40.             move.l    -(a1),d6        /* 21    */
  41. precalc:        eor.l        d1,d0
  42.             eor.l        d6,d1
  43.             or.l        d0,d1
  44.             eor.l        d6,d0
  45.             eor.l        d0,d1
  46.             addx.b    d5,d5
  47.             addx.l    d6,d6        /* 14    */
  48. mixed:        move.l    d0,d2
  49.             not.l        d0
  50.             addx.b    d5,d5
  51.             addx.l    d2,d2
  52.             move.l    d2,d4
  53.             addx.b    d5,d5
  54.             addx.l    d4,d4
  55.             not.l        d4
  56.             eor.l        d4,d0
  57.             or.l        d0,d4
  58.             eor.l        d0,d2
  59.             or.l        d2,d0
  60.             move.l    d1,d3
  61.             not.l        d1
  62.             addx.b    d5,d5
  63.             addx.l    d3,d3
  64.             eor.l        d3,d0
  65.             addx.b    d5,d5
  66.             addx.l    d3,d3
  67.             not.l        d3
  68.             eor.l        d3,d1
  69.             or.l        d1,d3
  70.             eor.l        d0,d4
  71.             or.l        d4,d0
  72.             or.l        d6,d2
  73.             eor.l        d1,d4
  74.             and.l        d4,d2
  75.             and.l        d0,d2
  76.             and.l        d3,d2
  77.             addx.b    d5,d5        /* 60    */
  78.             lsl.b        #3,d5
  79. fixshift:        lsr.l        #1,d2
  80.             exg        d2,a4
  81.             bcc        @store
  82.             bset        #31,d2        /* 13    */
  83. store:        move.l    d2,-(a3)        /* 5        */
  84. loop:            dbf        d7,@load        /* 6        */        /* == 123 */
  85.             move.l    a4,d0
  86.             movem.l    (sp)+,d3-d7/a2-a4
  87.             }
  88.     }
  89.  
  90. /*******************************************************
  91. *                                                                *
  92. *    fixEdges updates the opposite edges from each other.  This works out to be    *
  93. *    faster than including it in the main life routine, as this way it need not worry    *
  94. *    about where the edges are, or use any registers for this.  Also, this way        *
  95. *    fixEdges can be used with any simple automaton, ie one which has only a one-    *
  96. *    pixel neighborhood.                                                *
  97. *                                                                *
  98. *******************************************************/
  99.  
  100. fixEdges( theData )
  101.     dataPtr        theData;
  102.     {
  103.     char            *ldata, *rdata;
  104.     int            perline, h, lbit, rbit, which;
  105.  
  106.     which = -theData->offBits.bounds.left;
  107.     lbit = 32 - which;
  108.     which += theData->offBits.bounds.right - 1;
  109.     rbit = 31 - (( which & 15 ) + 1 );
  110.     ldata = theData->offBits.baseAddr;
  111.     rdata = ldata + (( which >> 3 ) & wordMask );
  112.     perline = theData->offBits.rowBytes;
  113.     h = theData->offBits.bounds.bottom - theData->offBits.bounds.top - 1;
  114.     asm        {
  115.             movem.l    d3-d7,-(sp)
  116.             move    h,d7
  117.             move    perline,d6
  118.             move    lbit,d5
  119.             move    rbit,d4
  120.             moveq    #0,d3
  121.             move.l    d3,d2
  122.             bset        d5,d3
  123.             bset        d4,d2
  124.             subq        #1,d5
  125.             addq        #1,d4
  126.             move.l    ldata,a1
  127.             move.l    rdata,a0
  128. loop:            move.l    (a1),d1
  129.             move.l    (a0),d0
  130.             btst        d5,d1
  131.             beq        @skiprnot
  132.             not.l        d0
  133.             btst        d4,d0
  134.             bne        @skiplnot
  135.             not.l        d1
  136.             bra        @skiplnot
  137. skiprnot:        btst        d4,d0
  138.             beq        @skiplnot
  139.             not.l        d1
  140. skiplnot:        and.l        d3,d1
  141.             and.l        d2,d0
  142.             eor.l        d1,(a1)
  143.             eor.l        d0,(a0)
  144.             add        d6,a1
  145.             add        d6,a0
  146.             dbf        d7,@loop
  147.             movem.l    (sp)+,d3-d7
  148.             }
  149.     }
  150.  
  151. /*******************************************************
  152. *                                                                *
  153. *    changeData executes as much of the stepping process on theData as possible    *
  154. *    in the given amount of time, and if it finishes one generation before it has used    *
  155. *    the time up, it changes the time to the amount that is left over.  The cases for    *
  156. *    the first and last lines are slightly different, as it must wrap above or below.    *
  157. *                                                                *
  158. *******************************************************/
  159.  
  160. changeData( theData, time )
  161.     dataPtr        theData;
  162.     int            *time;
  163. {
  164.     register int    high, wide, state;
  165.     long            lines, words, offset;
  166.     long            whole, *prev, *curr, *next, *dtop, *dest, *dend, *stop, oldval;
  167.     
  168.     state = theData->partDone;
  169.     wide = theData->offBits.rowBytes / 4;
  170.     high = theData->offBits.bounds.bottom - theData->offBits.bounds.top - 1;
  171.     lines = ( *time * lifeSpeed ) / wide;
  172.     lines = state ? min( state, lines ) : min( high, lines );
  173.     words = lines * wide;
  174.     *time -= words / lifeSpeed;
  175.     dtop = ( long* )theData->bases[ 1 ];
  176.     stop = ( long* )theData->bases[ 0 ];
  177.     if( !state ) {
  178.         fixEdges( theData );
  179.         offset = ( long )high * wide;
  180.         next = stop + wide;
  181.         prev = stop + offset;
  182.         curr = prev + wide;
  183.         dest = ( offset + wide + 1 ) + dtop;
  184.         oldval = lifeLines(( long )wide, prev, curr, next, dest, 0L );
  185.         if( lines > 1 )
  186.             oldval = lifeLines(( lines - 1) * wide, prev - wide, prev, curr, dest - wide, oldval );
  187.         state = high - lines;
  188.     } else {
  189.         offset = ( long )state * wide;
  190.         prev = offset + stop;
  191.         curr = prev + wide;
  192.         next = curr + wide;
  193.         dest = ( offset + wide ) + dtop;
  194.         oldval = *dest++;
  195.         oldval = lifeLines( lines * wide, prev, curr, next, dest, oldval );
  196.         state -= lines;
  197.     }
  198.     if( !state ) {
  199.         dest = dtop + wide + 1;
  200.         curr = stop + wide;
  201.         offset = ( long )high * wide;
  202.         *dtop = lifeLines(( long )wide, curr + offset, curr, curr + wide, dest, oldval );
  203.         state = -1;
  204.     } else *( dtop + ( long )( state + 1 ) * wide ) = oldval;
  205.     theData->partDone = state;
  206.  }
  207.  
  208. /*******************************************************
  209. *                                                                *
  210. *    StripEdges is used by checkCycles to remove extraneous pixels along the edges    *
  211. *    of theData, so that it is comparable with the saved image.                    *
  212. *                                                                *
  213. *******************************************************/
  214.  
  215. stripEdges( theData )
  216.     dataPtr        theData;
  217. {
  218.     char            *base;
  219.     long            maskl, maskr;
  220.     int            width, height;
  221.     
  222.     base = theData->bases[ 0 ];
  223.     width = theData->offBits.rowBytes - 4;
  224.     height = theData->offBits.bounds.bottom - theData->offBits.bounds.top - 1;
  225.     maskl = -1L >> -theData->offBits.bounds.left;
  226.     maskr = -1L << ( width * 8 - theData->offBits.bounds.right + theData->offBits.bounds.left );
  227.     asm        {
  228.             move    d3,a1
  229.             move.l    base,a0
  230.             move.l    maskl,d0
  231.             move.l    maskr,d1
  232.             move    width,d2
  233.             move    height,d3
  234. loop:            and.l        d0,(a0)
  235.             add        d2,a0
  236.             and.l        d1,(a0)+
  237.             dbf        d3,@loop
  238.             move    a1,d3
  239.             }
  240. }
  241.  
  242. /*******************************************************
  243. *                                                                *
  244. *    blockCompare is quite simple - it returns whether the data at the two pionters    *
  245. *    are the same or not.                                                *
  246. *                                                                *
  247. *******************************************************/
  248.  
  249. blockCompare( base0, base1, length )
  250.     char            *base0, *base1;
  251.     long            length;
  252. {
  253.     asm        {
  254.             move.l    base0,a0
  255.             move.l    base1,a1
  256.             move.l    length,d0
  257.             lsr.l        #2,d0
  258.             move    d0,d1
  259.             swap        d0
  260.             ori        #4,CCR        /* Z flag */
  261.             bra        @entry
  262. loop:            cmpm.l    (a0)+,(a1)+
  263. entry:        dbne        d1,@loop
  264.             dbne        d0,@loop
  265.             seq        d0
  266.             ext.w    d0
  267.             }
  268. }
  269.  
  270. /*******************************************************
  271. *                                                                *
  272. *    checkCycle is called once every cycleLength generations to check for cycles.    *
  273. *    If a cycle exists, the data in this case is restarted with a random pattern.  In    *
  274. *    future, more interesting things may be implimented, such as finding the point    *
  275. *    at which the data began to cycle (requiring another bitmap) or finding the actual    *
  276. *    (minimal) length of the cycle, by factoring the cycle length.                *
  277. *                                                                *
  278. *******************************************************/
  279.  
  280. checkCycle( theData )
  281.     dataPtr        theData;
  282. {
  283.     int            result;
  284.     char            *source, *dest;
  285.  
  286.     stripEdges( theData );
  287.     source = theData->bases[ 1 ];
  288.     dest = *( theData->cycleBase );
  289.     result = blockCompare( source, dest, theData->planeSize );
  290.     if( result ) {
  291.         randomize( &( theData->offBits ), source );
  292.         theData->generation = 0;
  293.     }
  294.     BlockMove( source, dest, theData->planeSize );
  295.     theData->cycleGeneration = theData->generation;
  296.     return( result );
  297. }
  298.  
  299. /*******************************************************
  300. *                                                                *
  301. *    copyData executes as much of the copying needed to bring a new generation to    *
  302. *    the screen as it can in the given time, assuming that gray is a region containing    *
  303. *    whatever gray is painted on the screen at the moment it is called.  finishCopy    *
  304. *    is called when a window is forced to stop, and is already partialy copied.  (if    *
  305. *    no data has been copied, the new data is simply thrown away).                *
  306. *                                                                *
  307. *******************************************************/
  308.  
  309. finishCopy( theData )
  310.     dataPtr        theData;
  311. {
  312.     int            temp = 0;
  313.  
  314.     copyData( theData, &temp, NULL );
  315. }
  316.  
  317. copyData( theData, time, gray )
  318.     dataPtr        theData;
  319.     int            *time;
  320.     RgnHandle        gray;
  321. {
  322.     Rect            bounds;
  323.     RgnHandle        maskRgn;
  324.     GrafPtr        savePort;
  325.     WindowPeek    theWindow;
  326.     int            high, wide, lines, yetToDo, toDraw = 0;
  327.  
  328.     GetPort( &savePort );
  329.     SetPort( theWindow = ( WindowPeek )( theData->parent ));
  330.     if( theData->partDone == -1 ) {
  331.         swapPlane( theData );
  332.         madeChange( theData );
  333.         ++( theData->generation );
  334.         toDraw = bitsBadGen;
  335.     }
  336.     if( !EmptyRgn( theWindow->updateRgn )) {
  337.         theData->partDone = 0;
  338.         displayData( theData, bitsBadData | toDraw, gray );
  339.         *time = 0;
  340.     } else {
  341.         if( toDraw ) displayData( theData, toDraw, gray );
  342.         high = theData->dataRect.bottom - theData->dataRect.top;
  343.         yetToDo = high + theData->partDone + 1;
  344.         
  345.         if( *time ) {
  346.             wide = theData->offBits.rowBytes / 4;
  347.             lines = ( *time * copySpeed ) / wide;
  348.             lines = min( lines, yetToDo );
  349.             *time -= (( long )lines * wide ) / copySpeed;
  350.         } else lines = yetToDo;
  351.         
  352.         bounds = theData->dataRect;
  353.         bounds.top += high - yetToDo;
  354.         bounds.bottom = bounds.top + lines;
  355.         if( theData->isSelect ) {
  356.             maskRgn = NewRgn();
  357.             RectRgn( maskRgn, &( theData->dataRect ));
  358.             DiffRgn( maskRgn, theData->selRgn, maskRgn );
  359.         } else maskRgn = NULL;
  360.         copyDisplay( &( theData->offBits ), &bounds, maskRgn, gray );
  361.         if( maskRgn ) DisposeRgn( maskRgn );
  362.         yetToDo -= lines;
  363.         theData->partDone = yetToDo ? -( high - yetToDo + 1 ) : 0;
  364.     }
  365.     if( theData->state == dataStepping && theData->partDone == 0 ) {
  366.         theData->state = dataStopped;
  367.         --isBackground;
  368.     }
  369.     SetPort( savePort );
  370. }
  371.  
  372. /*******************************************************
  373. *                                                                *
  374. *    StepData is called to while away a certain amount of time on one data element.    *
  375. *    If stops after it has finished copying new data to the screen, if it gets that far.    *
  376. *    It takes care of all parts of generating the next generation, including the cycle    *
  377. *    detection and 'auto restart'.  It is called with a dataHandle - all routines below    *
  378. *    it are called with pointers to the locked handle.                            *
  379. *                                                                *
  380. *******************************************************/
  381.  
  382. stepData( theDataHand, time, gray )
  383.     dataHandle    theDataHand;
  384.     RgnHandle        gray;
  385.     int            *time;
  386. {
  387.     dataPtr        theData;
  388.     
  389.     HLock( theDataHand );
  390.     theData = *theDataHand;
  391.     if( theData->partDone >= 0 ) {
  392.         changeData( theData, time );
  393.         if( theData->partDone == -1 && theData->cycleFlags != cycleNone &&
  394.                 theData->generation - theData->cycleGeneration == theData->cycleLength )
  395.             checkCycle( theData );
  396.     }
  397.     if( theData->partDone < 0 && *time )
  398.         copyData( theData, time, gray );
  399.     HUnlock( theDataHand );
  400. }
  401.  
  402. /*******************************************************
  403. *                                                                *
  404. *    DrawAnyAnts takes care of any crawly-ants (active selections) on-screen at    *
  405. *    the time it is called, which is once per call to doBackground.  It perhaps should    *
  406. *    be called every few ticks, but some prettyness in the crawly-ants is given up    *
  407. *    to gain efficiency in the other background jobs.  This must be changed if it is    *
  408. *    found that doBackground is called with extremely long waits.                *
  409. *                                                                *
  410. *******************************************************/
  411.  
  412. drawAnyAnts() {
  413.     dataHandle    theDataHand;
  414.     WindowPtr    theWindow;
  415.     dataPtr        theData;
  416.     int            hState, change;
  417.     
  418.     theWindow = frontUserWindow();
  419.     if( theWindow ) {
  420.         theDataHand = ( dataHandle )GetWRefCon( theWindow );
  421.         hState = HGetState( theDataHand );
  422.         HLock( theDataHand );
  423.         theData = *theDataHand;
  424.         if( theData->isSelect && theData->selActive && ( change = updateAnts( theData )))
  425.             drawAnts( theData, change );
  426.         HSetState( theDataHand, hState );
  427.     }
  428. }
  429.  
  430. /*******************************************************
  431. *                                                                *
  432. *    DoBackground is called as often as possible with the estimated amount of time    *
  433. *    available, a flag saying what the contents of theOutline consist of, and a region    *
  434. *    describing an area of the screen which is painted with gray.  GrayPart may be    *
  435. *    noGray, in which case theOutline should be NULL, someGray, in which case the    *
  436. *    global WindowPeek messyLevel is checked against each window, or allGray, in    *
  437. *    which case it is assumed that all windows may be trashed.                    *
  438. *        DoBackground also calls finishChannels to take care of sounds which may    *
  439. *    be queued by the sound patches.  This perhaps should only be done in the fore-    *
  440. *    ground, but that would seem counter-intuitive to the user.                    *
  441. *                                                                *
  442. *******************************************************/
  443.  
  444. doBackground( time, grayPart, theOutline )
  445.     int            time, grayPart;
  446.     RgnHandle        theOutline;
  447. {
  448.     dataHandle    nextDataHand, startData;
  449.     dataPtr        theData;
  450.     int            hState, doOutline;
  451.     
  452.     if( !isBackground ) return;
  453.     finishChannels();
  454.     drawAnyAnts();
  455.     startData = currentData;
  456.     if( currentData )
  457.         do {
  458.             hState = HGetState( currentData );
  459.             HLock( currentData );
  460.             theData = *currentData;
  461.             if( theData->state != dataStopped ) {
  462.                 doOutline = grayPart == allGray || ( grayPart == someGray && !cleanWindow( theData->parent ));
  463.                 stepData( currentData, &time, doOutline ? theOutline : NULL );
  464.             }
  465.             nextDataHand = theData->next;
  466.             HSetState( currentData, hState );
  467.             currentData = nextDataHand;
  468.         } while( time && currentData != startData );
  469. }